<template>
  <div class="my-scene-box" ref="mySceneBox"></div>
</template>

<script>
// 导入整个 three.js核心库
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import WebGL from "@/utils/WebGL.js";

export default {
  name: "demoOne",
  data() {
    return {
      scene: null, // 场景
      renderer: null, // 渲染器
      controls: null, // 轨道控制器
    };
  },
  mounted() {
    this.initThree();
  },
  methods: {
    initThree() {
      // 浏览器WebGL兼容性判断
      if (!WebGL.isWebGLAvailable()) {
        const warning = WebGL.getWebGLErrorMessage();
        this.$refs.mySceneBox.appendChild(warning);
        return;
      }
      // 1.创建场景对象Scene
      this.scene = new THREE.Scene();
      // 2.创建多个几何模型 并生产 mesh(网格模型) 数组
      const meshList = this.initGeometry();

      // 场景对象scene的方法.add()把网格模型mesh加入场景中
      meshList.forEach((mesh) => {
        this.scene.add(mesh); //网格模型添加到场景中  默认是在 （0，0，0）坐标
      });

      // 辅助坐标系  参数250表示坐标系大小，可以根据场景大小去设置
      // var axisHelper = new THREE.AxisHelper(250); // 构造函数改名了
      const axisHelper = new THREE.AxesHelper(250);
      this.scene.add(axisHelper);

      // 4.光源设置
      const light = this.initLight();
      this.scene.add(light);

      // 5.相机设置
      var width = this.$refs.mySceneBox.clientWidth; //窗口宽度
      var height = this.$refs.mySceneBox.clientHeight; //窗口高度
      var k = width / height; //窗口宽高比
      var s = 200; //三维场景显示范围控制系数，系数越大，显示的范围越大
      //创建相机对象
      this.camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
      this.camera.position.set(200, 300, 200); //设置相机位置
      this.camera.lookAt(this.scene.position); //设置相机方向(指向的场景对象)
      // 6.创建渲染器对象
      this.renderer = new THREE.WebGLRenderer();
      this.renderer.setSize(width, height); //设置渲染区域尺寸
      this.renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色

      this.$refs.mySceneBox.appendChild(this.renderer.domElement); //body元素中插入canvas对象

      //执行渲染操作   指定场景、相机作为参数
      this.loopRender();

      // 新增轨道控制器
      this.controls = new OrbitControls(this.camera, this.renderer.domElement); //创建控件对象

      // requestAnimationFrame 周期性执行 loopRender 函数，没必要再通过监听鼠标事件执行render函数
      // this.controls.addEventListener("change", this.loopRender); //监听鼠标、键盘事件
    },
    // 生成光源
    initLight() {
      /** 光源分类
       * 1.AmbientLight      环境光
       * 2.PointLight        点光源
       * 3.DirectionalLight  平行光（比如太阳光）
       * 4.SpotLight         聚光源
       *
       * 立体效果:仅仅使用环境光的情况下，你会发现整个立方体没有任何棱角感，这是因为环境光只是设置整个空间的明暗效果。如果需要立方体渲染要想有立体效果，需要使用具有方向性的点光源、平行光源等。
       */
      // 1.环境光    环境光颜色与网格模型的颜色进行RGB进行乘法运算
      // const ambient = new THREE.AmbientLight(0x444444);
      // const ambient = new THREE.AmbientLight(0xffffff);   // 这个更亮
      // return ambient;

      // 2.点光源  可以设置多个点光源
      //点光源
      const point = new THREE.PointLight(0xffffff);
      point.position.set(400, 200, 300); //点光源位置
      return point;
    },
    // 创建多个几何模型  返回 mesh 对象数组
    initGeometry() {
      // 1.创建一个立方体网格模型
      const geometry1 = new THREE.BoxGeometry(100, 100, 100); // 立方体几何模型
      const material1 = new THREE.MeshLambertMaterial({
        color: 0x0000ff, // 颜色
        opacity: 0.5, // 透明度
        transparent: true, // 是否透明 默认为false
      }); //材质对象Material
      const mesh1 = new THREE.Mesh(geometry1, material1); //网格模型对象Mesh

      // 2.创建一个基础立方体网格模型
      const geometry2 = new THREE.BoxGeometry(100, 100, 100); // 球面几何模型   SphereGeometry(radius, widthSegments, heightSegments)
      const material2 = new THREE.MeshBasicMaterial({ color: 0xff0000 }); //材质对象Material
      const mesh2 = new THREE.Mesh(geometry2, material2);
      mesh2.translateY(120); //球体网格模型沿Y轴正方向平移120

      // 3.创建一个漫反射立方体网格模型
      const geometry3 = new THREE.BoxGeometry(100, 100, 100); // 立方体几何模型
      const material3 = new THREE.MeshLambertMaterial({
        color: 0x0000ff, // 颜色
        // opacity: 0.5, // 透明度
        // transparent: true, // 是否透明 默认为false
        wireframe: true, // 将几何图形渲染为线框。 默认值为false  与透明度互斥
      }); //材质对象Material
      const mesh3 = new THREE.Mesh(geometry3, material3);
      mesh3.position.set(120, 0, 0); //设置mesh3模型对象的xyz坐标为120,0,0

      // three.js而言漫反射、镜面反射分别对应两个构造函数MeshLambertMaterial()、MeshPhongMaterial()
      // 4.创建一个高光立方体网格模型
      const geometry4 = new THREE.BoxGeometry(100, 100, 100); // 立方体几何模型
      const material4 = new THREE.MeshPhongMaterial({
        color: 0x0000ff,
        specular: 0x4488ee, //  属性specular表示球体网格模型的高光颜色，改颜色的RGB值会与光照颜色的RGB分量相乘
        shininess: 12, //  shininess属性可以理解为光照强度的系数
      }); //镜面反射
      const mesh4 = new THREE.Mesh(geometry4, material4);
      mesh4.position.set(240, 0, 0); //设置mesh3模型对象的xyz坐标为120,0,0

      return [mesh1, mesh2, mesh3, mesh4];

      /** 材质归类
       *  1.MeshBasicMaterial     基础网格材质，不受光照影响的材质
       *  2.MeshLambertMaterial   Lambert网格材质，与光照有反应，漫反射
       *  3.MeshPhongMaterial     高光Phong材质,与光照有反应
       *  4.MeshStandardMaterial  PBR物理材质，相比较高光Phong材质可以更好的模拟金属、玻璃等效果
       * */
    },

    // 渲染函数
    loopRender() {
      requestAnimationFrame(this.loopRender);
      this.renderer.render(this.scene, this.camera); //执行渲染操作
    },
  },
};
</script>
<style>
.my-scene-box {
  width: 90%;
  height: 500px;
  margin: 0 auto;
  border: 1px solid #000;
}
</style>
